using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.Collections.Generic;

namespace CodRevit.Commands
{
    /// <summary>
    /// 材质刷命令类 - 用于在不同元素面之间复制材质
    /// 支持 Revit 2020 和 2024 版本的兼容性
    /// </summary>
    [Transaction(TransactionMode.Manual)]
    public class MaterialBrushCommand : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            try
            {
                // 获取当前文档和UI文档
                UIDocument uidoc = commandData.Application.ActiveUIDocument;
                Document doc = uidoc.Document;
                
                // 【1】第一次选择 - 源元素
                Reference sourceRef;
                try
                {
                    // 让用户选择源材质面
                    sourceRef = uidoc.Selection.PickObject(ObjectType.Face, selectionFilter: new ElementMaterialFilter(),
                        "请选择源材质面");
                }
                catch (Autodesk.Revit.Exceptions.OperationCanceledException)
                {
                    // 用户取消选择，正常退出
                    return Result.Succeeded;
                }
                
                // 获取源元素及其材质信息
                Element sourceElement = doc.GetElement(sourceRef);
                Material sourceMaterial = null;
                
#if RVT_20
                // Revit 2020 使用 ElementId.InvalidElementId
                ElementId sourceMaterialId = ElementId.InvalidElementId;
#elif RVT_24
                // Revit 2024 使用 ElementId.InvalidElementId (保持兼容)
                ElementId sourceMaterialId = ElementId.InvalidElementId;
#else
                // 默认版本处理
                ElementId sourceMaterialId = ElementId.InvalidElementId;
#endif
                // 【2】处理选择到可载入族的面的材质
                if (sourceElement is FamilyInstance familyInstance)
                {
                    // 处理族实例的情况，通过reference获取面的几何对象
                    Face sourceFace = GetFaceFromReference(sourceElement, sourceRef);
                    if (sourceFace != null)
                    {
                        // 直接从面获取材质
                        sourceMaterialId = GetMaterialIdFromFace(sourceFace);
                        if (IsValidElementId(sourceMaterialId))
                        {
                            sourceMaterial = doc.GetElement(sourceMaterialId) as Material;
                        }
                    }
                }
                else if (sourceElement is Wall wall)
                {
                    // 处理墙体的情况 - 获取第一层材质
                    sourceMaterialId = wall.WallType.GetCompoundStructure().GetMaterialId(0);
                    sourceMaterial = doc.GetElement(sourceMaterialId) as Material;
                }
                else if (sourceElement is Floor floor)
                {
                    // 处理楼板的情况 - 获取第一层材质
                    sourceMaterialId = floor.FloorType.GetCompoundStructure().GetMaterialId(0);
                    sourceMaterial = doc.GetElement(sourceMaterialId) as Material;
                }
                else
                {
                    // 处理其他元素 - 尝试获取结构材质或普通材质参数
                    Parameter matParam = sourceElement.get_Parameter(BuiltInParameter.STRUCTURAL_MATERIAL_PARAM)
                        ?? sourceElement.get_Parameter(BuiltInParameter.MATERIAL_ID_PARAM);
                    if (matParam != null && matParam.HasValue)
                    {
                        sourceMaterialId = matParam.AsElementId();
                        sourceMaterial = doc.GetElement(sourceMaterialId) as Material;
                    }
                }
                // 检查是否成功获取源材质
                if (sourceMaterial == null)
                {
                    TaskDialog.Show("错误", "无法获取源面的材质");
                    return Result.Failed;
                }
                
                // 获取源材质的颜色属性，用于后续复制
                var materialColors = GetMaterialColors(sourceMaterial);
                // 第二次选择 - 目标面（循环选择多个目标）
                while (true)
                {
                    try
                    {
                        // 让用户选择目标面
                        Reference targetRef = uidoc.Selection.PickObject(ObjectType.Face, selectionFilter: new ElementMaterialFilter(),
                            "请选择要应用材质的目标面 (ESC键退出)");
                        Element targetElement = doc.GetElement(targetRef);
                        Material targetMaterial = null;
                        ElementId targetMaterialId = ElementId.InvalidElementId;
                        // 在事务中修改材质
                        using (Transaction trans = new Transaction(doc, "应用材质"))
                        {
                            if (targetElement is FamilyInstance targetFamilyInstance)
                            {
                                // 处理族实例的目标面
                                Face targetFace = targetElement.GetGeometryObjectFromReference(targetRef) as Face;
                                if (targetFace != null)
                                {
                                    bool found = false;
                                    // 启动事务
                                    trans.Start();

                                    // 找到面后，直接遍历族类型的材质参数
                                    FamilySymbol familySymbol = targetFamilyInstance.Symbol;
                                    foreach (Parameter param in familySymbol.GetOrderedParameters())
                                    {
                                        if (IsMaterialParameter(param))
                                        {
                                            // 临时设置材质参数来测试是否影响当前面
                                            ElementId originalMatId = param.AsElementId();
                                            param.Set(sourceMaterial.Id);
                                            doc.Regenerate();

                                            // 重新获取面的材质ID来验证
                                            Face testFace = GetFaceFromReference(targetElement, targetRef);
                                            if (testFace != null && GetMaterialIdFromFace(testFace) == sourceMaterial.Id)
                                            {
                                                // 找到正确的参数，保持修改
                                                found = true;
                                                break;
                                            }

                                            // 不是正确的参数，恢复原值
                                            param.Set(originalMatId);
                                            doc.Regenerate();
                                        }
                                    }

                                    if (!found)
                                    {
                                        TaskDialog.Show("提示", "未找到匹配的材质参数");
                                        trans.RollBack();
                                    }
                                    else
                                    {
                                        trans.Commit();
                                    }
                                }
                            }
                            else if (targetElement is Wall targetWall)
                            {
                                trans.Start();
                                // 处理墙体的目标面
                                CompoundStructure cs = targetWall.WallType.GetCompoundStructure();
                                if (cs != null)
                                {
                                    targetMaterialId = cs.GetMaterialId(0);
                                    cs.SetMaterialId(0, sourceMaterial.Id);
                                    targetWall.WallType.SetCompoundStructure(cs);

                                    // 如果目标材质存在且与源材质不同，复制颜色属性
                                    if (IsValidElementId(targetMaterialId) && targetMaterialId != sourceMaterial.Id)
                                    {
                                        targetMaterial = doc.GetElement(targetMaterialId) as Material;
                                        if (targetMaterial != null)
                                        {
                                            SetMaterialColors(targetMaterial, materialColors);
                                        }
                                    }
                                    trans.Commit();
                                }
                                else
                                {
                                    trans.RollBack();
                                }
                            }
                            else if (targetElement is Floor targetFloor)
                            {
                                trans.Start();
                                // 处理楼板的目标面
                                CompoundStructure cs = targetFloor.FloorType.GetCompoundStructure();
                                if (cs != null)
                                {
                                    targetMaterialId = cs.GetMaterialId(0);
                                    cs.SetMaterialId(0, sourceMaterial.Id);
                                    targetFloor.FloorType.SetCompoundStructure(cs);
                                    // 如果目标材质存在且与源材质不同，复制颜色属性
                                    if (IsValidElementId(targetMaterialId) && targetMaterialId != sourceMaterial.Id)
                                    {
                                        targetMaterial = doc.GetElement(targetMaterialId) as Material;
                                        if (targetMaterial != null)
                                        {
                                            SetMaterialColors(targetMaterial, materialColors);
                                        }
                                    }
                                    trans.Commit();
                                }
                                else
                                {
                                    trans.RollBack();
                                }
                            }
                            else
                            {
                                trans.Start();
                                // 处理其他元素的目标面
                                Parameter matParam = targetElement.get_Parameter(BuiltInParameter.STRUCTURAL_MATERIAL_PARAM)
                                    ?? targetElement.get_Parameter(BuiltInParameter.MATERIAL_ID_PARAM);
                                if (matParam != null)
                                {
                                    targetMaterialId = matParam.AsElementId();
                                    matParam.Set(sourceMaterial.Id);

                                    // 如果目标材质存在且与源材质不同，复制颜色属性
                                    if (IsValidElementId(targetMaterialId) && targetMaterialId != sourceMaterial.Id)
                                    {
                                        targetMaterial = doc.GetElement(targetMaterialId) as Material;
                                        if (targetMaterial != null)
                                        {
                                            SetMaterialColors(targetMaterial, materialColors);
                                        }
                                    }
                                    trans.Commit();
                                }
                                else
                                {
                                    trans.RollBack();
                                }
                            }
                        }
                    }
                    catch (Autodesk.Revit.Exceptions.OperationCanceledException)
                    {
                        // 用户按ESC取消选择，退出循环
                        break;
                    }
                }
                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                // 捕获并处理所有其他异常
                message = ex.Message;
                return Result.Failed;
            }
        }

        #region 版本兼容性辅助方法

        /// <summary>
        /// 从引用获取面对象 - 兼容不同版本
        /// </summary>
        private Face GetFaceFromReference(Element element, Reference reference)
        {
#if RVT_20
            return element.GetGeometryObjectFromReference(reference) as Face;
#elif RVT_24
            return element.GetGeometryObjectFromReference(reference) as Face;
#else
            return element.GetGeometryObjectFromReference(reference) as Face;
#endif
        }

        /// <summary>
        /// 从面获取材质ID - 兼容不同版本
        /// </summary>
        private ElementId GetMaterialIdFromFace(Face face)
        {
#if RVT_20
            return face.MaterialElementId;
#elif RVT_24
            return face.MaterialElementId;
#else
            return face.MaterialElementId;
#endif
        }

        /// <summary>
        /// 检查ElementId是否有效 - 兼容不同版本
        /// </summary>
        private bool IsValidElementId(ElementId elementId)
        {
#if RVT_20
            return elementId != ElementId.InvalidElementId;
#elif RVT_24
            return elementId != ElementId.InvalidElementId;
#else
            return elementId != ElementId.InvalidElementId;
#endif
        }

        /// <summary>
        /// 检查参数是否为材质类型 - 兼容不同版本
        /// </summary>
        private bool IsMaterialParameter(Parameter parameter)
        {
#if RVT_20
            return parameter.Definition.ParameterType == ParameterType.Material;
#elif RVT_24
            // Revit 2024 使用 GetDataType() 和 SpecTypeId
            return parameter.Definition.GetDataType() == SpecTypeId.Reference.Material;
#else
            return parameter.Definition.ParameterType == ParameterType.Material;
#endif
        }

        /// <summary>
        /// 获取材质颜色信息 - 兼容不同版本
        /// </summary>
        private MaterialColors GetMaterialColors(Material material)
        {
            return new MaterialColors
            {
#if RVT_20
                Color = material.Color,
                SurfaceForegroundPatternColor = material.SurfaceForegroundPatternColor,
                CutForegroundPatternColor = material.CutForegroundPatternColor
#elif RVT_24
                Color = material.Color,
                SurfaceForegroundPatternColor = material.SurfaceForegroundPatternColor,
                CutForegroundPatternColor = material.CutForegroundPatternColor
#else
                Color = material.Color,
                SurfaceForegroundPatternColor = material.SurfaceForegroundPatternColor,
                CutForegroundPatternColor = material.CutForegroundPatternColor
#endif
            };
        }

        /// <summary>
        /// 设置材质颜色信息 - 兼容不同版本
        /// </summary>
        private void SetMaterialColors(Material material, MaterialColors colors)
        {
#if RVT_20
            material.Color = colors.Color;
            material.SurfaceForegroundPatternColor = colors.SurfaceForegroundPatternColor;
            material.CutForegroundPatternColor = colors.CutForegroundPatternColor;
#elif RVT_24
            material.Color = colors.Color;
            material.SurfaceForegroundPatternColor = colors.SurfaceForegroundPatternColor;
            material.CutForegroundPatternColor = colors.CutForegroundPatternColor;
#else
            material.Color = colors.Color;
            material.SurfaceForegroundPatternColor = colors.SurfaceForegroundPatternColor;
            material.CutForegroundPatternColor = colors.CutForegroundPatternColor;
#endif
        }

        #endregion
    }

    /// <summary>
    /// 材质颜色信息结构体
    /// </summary>
    public struct MaterialColors
    {
        public Color Color { get; set; }
        public Color SurfaceForegroundPatternColor { get; set; }
        public Color CutForegroundPatternColor { get; set; }
    }

    /// <summary>
    /// 元素材质过滤器类 - 用于筛选具有材质的元素
    /// </summary>
    public class ElementMaterialFilter : ISelectionFilter
    {
        public bool AllowElement(Element element)
        {
            // 允许墙、楼板和族实例
            if (element is Wall || element is Floor || element is FamilyInstance)
                return true;
            
            // 检查其他元素是否具有材质参数
            Parameter matParam = element.get_Parameter(BuiltInParameter.STRUCTURAL_MATERIAL_PARAM)
                ?? element.get_Parameter(BuiltInParameter.MATERIAL_ID_PARAM);
            return matParam != null && matParam.HasValue;
        }

        /// <summary>
        /// 判断引用是否可被选择
        /// </summary>
        public bool AllowReference(Reference refer, XYZ point)
        {
            return true;
        }
    }
}